using QTool.Binary;
using QTool.Inspector;
using QTool.NetFrame.Core;
using System;
using System.Collections.Generic;
using System.Net;
using UnityEngine;
#if InputSystem
using UnityEngine.InputSystem;
#endif
using System.Threading.Tasks;
#if QSteamworks
using QSteamworks;
#endif
using UnityEngine.SceneManagement;
namespace QTool.NetFrame
{

	public class QNetManager : InstanceBehaviour<QNetManager>,IQSerialize
    {
		static QNetManager()
		{
			Command.QCommand.FreshCommands(typeof(QFixNode));
		}
        [Range(10, 30)]
        [ViewName("物理帧率")]
        public int physicsFps = 20;
        [Range(1,10)]
        [ViewName("物理/网络帧率倍率")]
        public int physicsDivNetFps= 2;
        [ViewName("同步检测间隔帧数")]
        [Range(50, 1000)]
        public int syncCheckFrame = 50;
        [ViewName("Port端口")]
        public int gameId = 12345;
        [ViewName("使用快速连接")]
        public bool useFastConnect=false;

		public static bool Active { get => Instance != null && !string.IsNullOrEmpty(Instance.hostIp); }
		public int PlayerId { private set; get; } = -1;
		public bool IsHost
		{
			get
			{
				return PlayerId == 0;
			}
		}
		protected QControlProtocol network { set; get; }
        public void StartNetwork()
        {
            if (network == null)
            {
                network = new QControlProtocol();
                network.OnMessageReceive += OnReceive;
                network.StartReceive();
            }
            else
            {
                network.OnMessageReceive += OnReceive;
            }
            
        }
        protected QNetConnectId serverIp;
        public QNetFrameEntity player;

        public FixRandom random = new FixRandom();
		/// <summary>
		/// 发送重传检测消息
		/// </summary>
        public void Send(byte[] bytes)
        {
            network.Send(bytes, MsgType.RSendCheck, serverIp);
        }
		/// <summary>
		/// 加入房间
		/// </summary>
		/// <param name="key">房间Id</param>
        public void JointRoom(string key)
        {
            if (!string.IsNullOrWhiteSpace( hostIp )) return;
            Send(QNetFrameRoom.GetSendBytes(  ServerMessageType.JoinRoom,(w)=> {
                w.Write(SystemInfo.deviceUniqueIdentifier);
                w.Write(playerName);
                w.Write(PlayerData);

            }, key));
        }
       
        protected void OnReceive(byte[] bytes,QNetConnectId id)
        {
            using (QBinaryReader reader = new QBinaryReader(bytes))
            {
                var type = (ServerMessageType)reader.ReadByte();
                var ip = reader.ReadString();
                switch (type)
                {
           
                    case ServerMessageType.RoomInfo:
                        {
                            var roomInfo = new MiniRoomInfo();
                            reader.ReadObject(roomInfo);
                            serverIp = id;
                            Debug.Log("房间信息[" + roomInfo.Key+"]");
                            JointRoom(roomInfo.Key);
                        }
                        break;
                    case ServerMessageType.JoinRoomOver:
                        {
                            hostIp = ip;
                            PlayerId = reader.ReadInt32();
                            random = new FixRandom(reader.ReadInt32());
                            NetTime = 0;
                            if (useFastConnect)
                            {
                                fastConnectData = reader.ReadBytes();
                            }
                            OnStart?.Invoke();
							Debug.Log("加入房间成功" + ip);

                            if (useFastConnect)
                            {
                                if (fastConnectData.Length > 0)
                                {
                                    QSerialize.Deserialize(fastConnectData, this);
                                }
                            }
                        }
                        break;
              
                    case ServerMessageType.FrameInfo:
                        {
                            var frame = reader.ReadObject<FrameData>();
                            frameList[frame.Key] = frame;
                        }
                        break;
                    case ServerMessageType.FrameInfoList:
                        {
                            var frames = reader.ReadObject<List<FrameData>>();
                            foreach (var frame in frames)
                            {
                                frameList[frame.Key] = frame;
                            }
                        }
                        break;
                    default:
                    //    Debug.LogError("【客户端】错误消息类型：" + type);
                        break;
                }
            }
         
        }

        byte[] fastConnectData = new byte[0];
        public QAutoList<byte, InputActions> Inputs = new QAutoList<byte, InputActions>();
        public QList<int,FrameData> frameList = new QList<int,FrameData>();
        public QDictionary<int, MiniClientInfo> PlayerList = new QDictionary<int, MiniClientInfo>();
     
        public int frameIndex { private set; get; } = 0;
      
#if QSteamworks
		[ViewName("使用Steam网络联机")]
        public bool useSteamNet;
#endif
		public QNetFrameRoom room;
		public async Task StartServer()
        {
            if (!serverIp.IsNull) return;

            room = new QNetFrameRoom(gameId, (int)(DelatTime * physicsDivNetFps * 1000));
#if QSteamworks
            if (useSteamNet)
            {
                await QSteam.CreateLobby(gameId.ToString());
                serverIp = QSteam.CurLobby.steamID;
            }
            else

#endif
            {

                serverIp = room.network.LocalId;
            }
            network = room.network;
        }
        private void OnDestroy()
        {
            Application.wantsToQuit -= QuitEvent;
            room?.Stop();
            network?.Close();
        }
        [ViewButton("创建主机",30, "IsPlaying")]
        public async void StartHost()
        {
            if (!Application.isPlaying)
            {
                return;
            }
            if (Active)
            {
                UnityEngine.Debug.LogError("NetManager已启动");
                return;
            }
            await StartServer();
            Connect();
			Debug.Log("启动主机");
        }
        [ViewButton("单人游戏", 30, "IsPlaying")]
        public async void StartSingleGame()
        {
            if (!Application.isPlaying)
            {
                return;
            }
            if (Active)
            {
                UnityEngine.Debug.LogError("NetManager已启动");
                return;
            }
          
#if QSteamworks
                useSteamNet = false;
#endif
            await StartServer();

            Connect();
			Debug.Log("启动单机游戏");
        }
        public bool IsPlaying
        {
            get
            {
                return Application.isPlaying;
            }
        }
        [ViewName("主机", "IsPlaying")]
        [ReadOnly]
        public string hostIp = "";
     
        bool QuitEvent()
        {
            hostIp = "";
            return true;
        }
        [ViewButton("快速加入游戏", 30, "IsPlaying")]
        public void StartClient()
        {
            if (!Application.isPlaying)
            {
                return;
            }
            if (Active)
            {
                UnityEngine.Debug.LogError("NetManager已启动");
                return;
            }
			Debug.Log("启动客户端");
            Connect();
        }
     
        private async void Connect()
        {
            Application.wantsToQuit += QuitEvent;
            StartNetwork();
            Application.runInBackground = true;
#if QSteamworks
            if (useSteamNet)
            {
                if (serverIp.IsNull)
                {
					if (await QSteam.FastJoin(gameId.ToString()))
					{
						Debug.Log("加入房间成功" + QSteam.CurLobby.steamID + "【" + QSteam.CurLobby.owner + "】" + QSteam.CurLobby.owner);
						network.Send(QNetFrameRoom.GetSendBytes(ServerMessageType.GetRoomInfo), MsgType.RSendCheck, QSteam.CurLobby.owner);
					}
					else
					{
						Debug.LogError("快速加入房间失败");
					}
				}
                else
                {
                    if (await QSteam.JoinLobby(serverIp))
                    {
						Debug.Log("加入房间成功" + serverIp);
                        network.Send(QNetFrameRoom.GetSendBytes(ServerMessageType.GetRoomInfo), MsgType.Normal, QSteam.CurLobby.owner);
                        return;
                    }
                    else
                    {
                        Debug.LogError("加入房间失败" + serverIp);
                    }
                }
               
            }
            else

#endif
            if( serverIp.IsNull)
            {
                network?.Broadcast(QNetFrameRoom.GetSendBytes(ServerMessageType.GetRoomInfo), gameId);
            }
            else
            {
                network?.Send(QNetFrameRoom.GetSendBytes(ServerMessageType.GetRoomInfo), MsgType.Normal,  new IPEndPoint(IPAddress.Loopback, gameId));
            }
            

        }

        private void OnGUI()
        {
            if (network != null) {
                GUI.TextArea(new Rect(0, 0, 600, 20), "当前帧" + frameIndex + "/" + frameList.Count + "    发送：" + network.SendSize.ToString(ArrayTool.ToSizeString) + "    接收：" + network.ReceiveSize.ToString(ArrayTool.ToSizeString) + "    延迟:" + network.Delay.Value);
            }
            if (room?.network != null)
            {
                GUI.TextArea(new Rect(0, 20, 600, 20), "当前帧" + room.frameList.Count + "    发送：" + room.network.SendSize.ToString(ArrayTool.ToSizeString) + "    接收：" + room.network.ReceiveSize.ToString(ArrayTool.ToSizeString) + "    延迟:" + room.network.Delay.Value);
            }
          
        }
        public Fix64 NetTime { private set; get; }
        public Fix64 DelatTime { private set; get; }
        protected override async void Awake()
        {
#if QSteamworks
            if (useSteamNet)
            {
                if (!await Tool.Wait(() => SteamManager.Initialized)) return;
            }
            if (SteamManager.Initialized)
            {
				Debug.Log("Steam用户[" + Steamworks.SteamFriends.GetPersonaName() + "]");
                playerName = Steamworks.SteamFriends.GetPersonaName();
            }
            else
#endif
            {
                playerName = SystemInfo.deviceName;
            }
            DelatTime = (Fix64)1 / physicsFps;
            Time.fixedDeltaTime = (float)DelatTime;
            Physics.autoSimulation = false;
            FixPhysics.space.TimeStepSettings.TimeStepDuration = DelatTime;
            if (player != null)
            {
                QIdPrefabs.objDic[player.PrefabId] = player.gameObject;
            }
            base.Awake();
        }
        string playerName;
        public static string PlayerData { get; set; } = "";
        public void Start()
        {
            InitInput();
            var infos = System.Environment.GetCommandLineArgs();
            DontDestroyOnLoad(gameObject);
            if (infos.Length > 1)
            {
                switch (infos[1])
                {
                    case "server":
						{
							playerName = "TestHost";
							StartHost();
                        }
                        break;
                    case "client":
                        {
							playerName = "TestClient";
							StartClient();
                        }
                        break;
                    default:
                        break;
                }
            }
        }
        public byte frameStep { get; private set; }=0;
        FrameData curFrame;
        void PhysicsUpdate()
        {
            Physics.Simulate((float)DelatTime);
            FixPhysics.Update();
        }
        internal bool LoadingScene()
        {
            if (fixScene == null)
            {
                return true;
            }
            else if(!fixScene.LoadOver)
            {
                fixScene.Load();
            }
            if (!string.IsNullOrWhiteSpace(nextScene))
            {
                for (int i = 0; i < NetObjList.Count; i++)
                {
                    var netObj = NetObjList[i];
                    if (netObj.ParentId==null&&!netObj.DontDestroyOnLoad)
                    {
                        FixDestroy(netObj);
                    }
                }
                SceneManager.LoadScene(nextScene);
                OnLoadScene?.Invoke(nextScene);
                nextScene = "";
                fixScene = null;
            }
            return false;
        }
        public static FixScene fixScene;
        bool NetUpdate(bool sendInput = true)
        {
            curFrame = frameList[frameIndex];
            if (curFrame == null) return false;
            if (LoadingScene()) return false;
            var startTime = DateTime.Now;
            for (int i = 0; i < awakeList.Count; i++)
            {
                var netObj = awakeList[i];
                if (netObj != null)
                {
                    netObj.OnFixAwake();
                }
            }
            awakeList.Clear();
            for (int i = 0; i < destroyList.Count; i++)
            {
                var netObj = destroyList[i];
                netObj.OnFixDestroy();
                if (netObj != null)
                {
                    Destroy(netObj.gameObject);
                }
            }
            destroyList.Clear();
        
            if (frameStep == 0)
            {
                if (sendInput)
                {
                  //  lock (inputList)
                    {
                        Send(QNetFrameRoom.GetSendBytes(ServerMessageType.InputInfo, (writer) =>
                        {
                            writer.WriteObject(inputList);
                        }));
                        inputList.Clear();
                    }
                }
                foreach (var inputList in curFrame.InputLists)
                {
                    var input = Inputs[inputList.Key];
                    foreach (var inputEvent in inputList)
                    {
                        input.Trigger(inputEvent);
                    }
                };

            }
			foreach (var netObj in NetObjList)
			{
				if (!netObj.FixActive) continue;
				netObj.OnFixUpdate();
			}
            PhysicsUpdate();
            frameStep++;
            if (frameStep == physicsDivNetFps)
            {
                frameStep = 0;
                frameIndex++;
                if (frameIndex % syncCheckFrame==0)
                {
                    syncCheckFlag.framIndex = frameIndex;
                    syncCheckFlag.flag = 0;
                    foreach (var obj in NetObjList)
                    {
                        syncCheckFlag.flag=QSerialize.Serialize(obj.fixEntity.Position).ToCheckFlag( syncCheckFlag.flag);
                        syncCheckFlag.flag =QSerialize.Serialize(obj.fixEntity.Rotation).ToCheckFlag( syncCheckFlag.flag);
                    }
                    SetInput(InputEvent.SyncCheck, syncCheckFlag, false);
                }
            }
            NetTime += DelatTime;
            return true;
        }
        struct SyncCheckFlag
        {
            public int framIndex;
            public byte flag;
        }
        SyncCheckFlag syncCheckFlag;
        public ActionEvent OnStart;
        private void FixedUpdate()
        {
      
            if (!Active) return;

            if (frameList.Count - frameIndex > 2)
            {
                var count = 0;
                while (frameList.Count - frameIndex>1)
                {
                    if (!NetUpdate(false)||count>=20*physicsDivNetFps)
                    {
                        return;
                    }
                    count++;
                }
            }
            if (network != null)
            {
                if (frameList.Count > frameIndex )
                {
                    NetUpdate();
                }
            }
        
        }
#if InputSystem
        public List<InputActionReference> syncInputList = new List<InputActionReference>();
#endif
       
        public static Dictionary<string, byte> InputIndex = new Dictionary<string, byte>();
        InputList inputList = new InputList();
        public static Dictionary<string, Type> valueType = new Dictionary<string, Type>
        {
            {"Vector2",typeof(Vector2) },
            {"Single",typeof(float) },
            {"Vector3",typeof(Vector3) },
            {"Integer",typeof(int) },
            {"Button",typeof(float) }
        };
        public event Action<string> OnLoadScene;
        string nextScene;
        public void LoadScene(string name)
        {
            SetInput(InputEvent.LoadScene, name, false);
        }
        public void InitInput() {
            InputActions.PlayerEvent += (id,playerEvent) =>
            {
                switch (playerEvent.id)
                {
                    case InputEvent.AddPlayer:
                        {
                            PlayerList[id] = QSerialize.Deserialize<MiniClientInfo>(playerEvent.bytes);
                            if (player != null)
                            {
                                var newPlayer= FixInstantiate(player.gameObject, FixVector3.Right* id, FixQuaternion.Identity,(netObj) => { netObj.playerId = id;});
                                newPlayer.gameObject.InvokeEvent("玩家名", PlayerList[id].playerName);
                                newPlayer.gameObject.InvokeEvent("玩家数据", PlayerList[id].playerData);
                            }
                            else
                            {
                                Debug.LogError("Player预制体为空");
                            }

                        }
                        return;
                    case InputEvent.LoadScene:
                        {
                            var name = playerEvent.ReadString();
                            nextScene = name;
                       
                        }
                        return;
                    case InputEvent.SyncCheck:
                        {
                            var check = QSerialize.Deserialize<SyncCheckFlag>(playerEvent.bytes);
                            if (PlayerId == id) return;
                            if (check.framIndex != syncCheckFlag.framIndex) return;
                            if (check.flag==syncCheckFlag.flag)
                            {
								Debug.Log(check.framIndex + " 与Player[" + id + "]同步校验通过");
                                
                            }
                            else
                            {
                                Debug.LogError(check.framIndex + " 与Player[" + id + "]不同步！！！！！！！！！！");
                            }
                        }
                        return;
                    default:
                        break;
                }
              
            };
            InputIndex.Clear();
#if InputSystem
            for (int index = 0; index < syncInputList.Count; index++)
            {
                var input = syncInputList[index];
                var action = input.action;
                var key = input.action.name.Substring(input.action.name.LastIndexOf('/')+1);
                if (!InputIndex.ContainsKey(key))
                {
                    InputIndex.Add(key, (byte)index);
                }
                action.Enable();
                input.action.performed += (content) =>
                {
                    var obj = input.action.ReadValueAsObject();
                    SetInput((byte)InputIndex[key], obj, valueType[input.action.expectedControlType],true);
                };
                input.action.canceled += (content) =>
                {
                    var obj = input.action.ReadValueAsObject();
                    SetInput((byte)InputIndex[key], obj, valueType[input.action.expectedControlType],false);
                };
            }
#endif
        }
        static QDictionary<byte, bool> inputActiveBuffer = new QDictionary<byte, bool>();
        private static void SetInput(byte id, object obj, Type type,bool active)
        {
            if (!Active) return;
            if (type == typeof(Vector2))
            {
                if (obj != null)
                    obj = (FixVector2)(Vector2)(obj);
                type = typeof(FixVector2);
            }
            else if (type == typeof(Vector3))
            {
                if (obj != null)
                    obj = (FixVector3)(Vector3)(obj);
                type = typeof(FixVector3);
            }
            else if (type == typeof(float))
            {
                if (obj != null)
                    obj = (Fix64)(float)(obj);
                type = typeof(Fix64);
            }
            else if (type == typeof(double))
            {
                if (obj != null)
                    obj = (Fix64)(double)(obj);
                type = typeof(Fix64);
            }
            var inputData = new InputEvent
            {
                id = id,
                bytes = obj == null ? null : QSerialize.SerializeType(obj, type)
            };

            if (!Instance.inputList.ContainsKey(id)||inputActiveBuffer[id] != active)
            {
                Instance.inputList.Add(inputData);
                inputActiveBuffer[id] = active;
            }
            else
            {
                Instance.inputList.Set(id, inputData);
            }
        }
        public static void SetInput<T>(byte id,T value,bool active=true)
        {
            SetInput(id, value, typeof(T), active);
        }
      
     
        internal static QList<string,QNetFrameEntity> NetObjList = new QList<string, QNetFrameEntity>();
        static List<QNetFrameEntity> awakeList = new List<QNetFrameEntity>();
        internal static List<QNetFrameEntity> destroyList = new List<QNetFrameEntity>();
        internal static void NetStart(QNetFrameEntity netObj)
        {
            if (netObj.FixActive)
            {
                return;
            }
            awakeList.AddCheckExist(netObj);
        }
        public static GameObject FixInstantiate(GameObject prefab, FixVector3 position, FixQuaternion rotation, Action<QNetFrameEntity> initCall = null)
        {
            var obj = GameObject.Instantiate(prefab, ((Vector3)position), ((Quaternion)rotation));
            var id = obj.GetComponent<QNetFrameEntity>();
            id.fixEntity.Position = position;
            id.fixEntity.Rotation = rotation;
            initCall?.Invoke(id);
            NetStart(id);
            return obj;
        }
        internal static void FixDestroy(QNetFrameEntity netObj)
        {
            if (destroyList.Contains(netObj))
            {
                return;
            }
            if (!NetObjList.Contains(netObj))
            {
                return;
            }
            destroyList.AddCheckExist(netObj);
        }
        [ViewButton("存档测试")]
        public void SaveTest()
        {
            QSerialize.Deserialize(QSerialize.Serialize(this), this);
        }
        public void Write(QBinaryWriter writer)
        {
            writer.Write(random.Seed);
            writer.Write(frameIndex);
            writer.WriteObject(NetTime);
            writer.Write(NetObjList.SaveAllInstance());
        }

        public void Read(QBinaryReader reader)
        {
            random.Seed = reader.ReadInt32();
            frameIndex = reader.ReadInt32();
            NetTime= reader.ReadObject(NetTime);
            NetObjList.LoadAllInstance(reader.ReadBytes());
        }
    }
   
    public class FixWaitTime : WaitTime<Fix64>
    {
    }
    public static class FixRandomTool
    {
        public static T RandomGet<T>(this IList<T> list,FixRandom random) 
        {
            return list[random.Range(list.Count - 1)];
        }
        public static IList<T> Random<T>(this IList<T> list, FixRandom random)
        {
            for (int i = 0; i < list.Count; i++)
            {
                var cur = list[i];
                list.Remove(cur);
                list.Insert(random.Range(i), cur);
            }
            return list;
        }
    }

    public class MiniRoomInfo : IKey<string>, IQSerialize
    {

        public string Key
        {
            get;
            set;
        }

        public void Write(QBinaryWriter write)
        {
            
        }
        public void Read(QBinaryReader read)
        {
            Key = read.ReadString();
        }

        public override string ToString()
        {
            return "房间[" + Key + "]";
        }
	}
	public class MiniClientInfo : IQSerialize
	{
		public string playerName;
		public string playerData;
		public void Read(QBinaryReader reader)
		{
			playerName = reader.ReadString();
			playerData = reader.ReadString();
		}

		public void Write(QBinaryWriter writer)
		{
			throw new NotImplementedException();
		}
	}

}
